gfx/layers/composite/LayerManagerComposite.cpp
author Greg Mierzwinski <gmierz2@outlook.com>
Sun, 16 Jul 2017 19:06:05 -0400 (2017-07-16)
changeset 1197665 1a1b876e87a9fd9647cb5921948bc770f1fdb585
parent 1168245 aa1693a26a15eb5cba24102222687dc81eeddd7b
child 1171640 f9125e325f860702dd8884307648e8247c90ec33
child 1180854 be5554329c8437985cfb380b68a2a9b43e1f6625
permissions -rw-r--r--
try: -b o -p linux64-ccov -u mochitest-dt -t none MozReview-Commit-ID: AnpETpQVW3Q
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "LayerManagerComposite.h"
#include <stddef.h>                     // for size_t
#include <stdint.h>                     // for uint16_t, uint32_t
#include "CanvasLayerComposite.h"       // for CanvasLayerComposite
#include "ColorLayerComposite.h"        // for ColorLayerComposite
#include "CompositableHost.h"           // for CompositableHost
#include "ContainerLayerComposite.h"    // for ContainerLayerComposite, etc
#include "Diagnostics.h"
#include "FPSCounter.h"                 // for FPSState, FPSCounter
#include "FrameMetrics.h"               // for FrameMetrics
#include "GeckoProfiler.h"              // for profiler_*
#include "ImageLayerComposite.h"        // for ImageLayerComposite
#include "Layers.h"                     // for Layer, ContainerLayer, etc
#include "LayerScope.h"                 // for LayerScope Tool
#include "protobuf/LayerScopePacket.pb.h" // for protobuf (LayerScope)
#include "PaintedLayerComposite.h"      // for PaintedLayerComposite
#include "TiledContentHost.h"
#include "Units.h"                      // for ScreenIntRect
#include "UnitTransforms.h"             // for ViewAs
#include "apz/src/AsyncPanZoomController.h"  // for AsyncPanZoomController
#include "gfxPrefs.h"                   // for gfxPrefs
#ifdef XP_MACOSX
#include "gfxPlatformMac.h"
#endif
#include "gfxRect.h"                    // for gfxRect
#include "gfxUtils.h"                   // for frame color util
#include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
#include "mozilla/RefPtr.h"             // for RefPtr, already_AddRefed
#include "mozilla/gfx/2D.h"             // for DrawTarget
#include "mozilla/gfx/Matrix.h"         // for Matrix4x4
#include "mozilla/gfx/Point.h"          // for IntSize, Point
#include "mozilla/gfx/Rect.h"           // for Rect
#include "mozilla/gfx/Types.h"          // for Color, SurfaceFormat
#include "mozilla/layers/Compositor.h"  // for Compositor
#include "mozilla/layers/CompositorTypes.h"
#include "mozilla/layers/Effects.h"     // for Effect, EffectChain, etc
#include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper
#include "mozilla/layers/LayersTypes.h"  // for etc
#include "mozilla/widget/CompositorWidget.h" // for WidgetRenderingContext
#include "ipc/CompositorBench.h"        // for CompositorBench
#include "ipc/ShadowLayerUtils.h"
#include "mozilla/mozalloc.h"           // for operator new, etc
#include "nsAppRunner.h"
#include "mozilla/RefPtr.h"                   // for nsRefPtr
#include "nsCOMPtr.h"                   // for already_AddRefed
#include "nsDebug.h"                    // for NS_WARNING, NS_RUNTIMEABORT, etc
#include "nsISupportsImpl.h"            // for Layer::AddRef, etc
#include "nsPoint.h"                    // for nsIntPoint
#include "nsRect.h"                     // for mozilla::gfx::IntRect
#include "nsRegion.h"                   // for nsIntRegion, etc
#if defined(MOZ_WIDGET_ANDROID)
#include <android/log.h>
#include <android/native_window.h>
#include "mozilla/widget/AndroidCompositorWidget.h"
#include "opengl/CompositorOGL.h"
#include "GLConsts.h"
#include "GLContextEGL.h"
#include "GLContextProvider.h"
#include "mozilla/Unused.h"
#include "mozilla/widget/AndroidCompositorWidget.h"
#include "ScopedGLHelpers.h"
#endif
#include "GeckoProfiler.h"
#include "TextRenderer.h"               // for TextRenderer
#include "mozilla/layers/CompositorBridgeParent.h"
#include "TreeTraversal.h"              // for ForEachNode

#ifdef USE_SKIA
#include "PaintCounter.h"               // For PaintCounter
#endif

class gfxContext;

namespace mozilla {
namespace layers {

class ImageLayer;

using namespace mozilla::gfx;
using namespace mozilla::gl;

static LayerComposite*
ToLayerComposite(Layer* aLayer)
{
  return static_cast<LayerComposite*>(aLayer->ImplData());
}

static void ClearSubtree(Layer* aLayer)
{
  ForEachNode<ForwardIterator>(
      aLayer,
      [] (Layer* layer)
      {
        ToLayerComposite(layer)->CleanupResources();
      });
}

void
LayerManagerComposite::ClearCachedResources(Layer* aSubtree)
{
  MOZ_ASSERT(!aSubtree || aSubtree->Manager() == this);
  Layer* subtree = aSubtree ? aSubtree : mRoot.get();
  if (!subtree) {
    return;
  }

  ClearSubtree(subtree);
  // FIXME [bjacob]
  // XXX the old LayerManagerOGL code had a mMaybeInvalidTree that it set to true here.
  // Do we need that?
}

HostLayerManager::HostLayerManager()
  : mDebugOverlayWantsNextFrame(false)
  , mWarningLevel(0.0f)
  , mCompositorBridgeID(0)
  , mWindowOverlayChanged(false)
  , mLastPaintTime(TimeDuration::Forever())
  , mRenderStartTime(TimeStamp::Now())
{}

HostLayerManager::~HostLayerManager()
{}

void
HostLayerManager::RecordPaintTimes(const PaintTiming& aTiming)
{
  mDiagnostics->RecordPaintTimes(aTiming);
}

void
HostLayerManager::RecordUpdateTime(float aValue)
{
  mDiagnostics->RecordUpdateTime(aValue);
}

/**
 * LayerManagerComposite
 */
LayerManagerComposite::LayerManagerComposite(Compositor* aCompositor)
: mUnusedApzTransformWarning(false)
, mDisabledApzWarning(false)
, mCompositor(aCompositor)
, mInTransaction(false)
, mIsCompositorReady(false)
#if defined(MOZ_WIDGET_ANDROID)
, mScreenPixelsTarget(nullptr)
#endif // defined(MOZ_WIDGET_ANDROID)
{
  mTextRenderer = new TextRenderer();
  mDiagnostics = MakeUnique<Diagnostics>();
  MOZ_ASSERT(aCompositor);

#ifdef USE_SKIA
  mPaintCounter = nullptr;
#endif
}

LayerManagerComposite::~LayerManagerComposite()
{
  Destroy();
}


void
LayerManagerComposite::Destroy()
{
  if (!mDestroyed) {
    mCompositor->GetWidget()->CleanupWindowEffects();
    if (mRoot) {
      RootLayer()->Destroy();
    }
    mCompositor->CancelFrame();
    mRoot = nullptr;
    mClonedLayerTreeProperties = nullptr;
    mDestroyed = true;

#ifdef USE_SKIA
    mPaintCounter = nullptr;
#endif
  }
}

void
LayerManagerComposite::UpdateRenderBounds(const IntRect& aRect)
{
  mRenderBounds = aRect;
}

bool
LayerManagerComposite::AreComponentAlphaLayersEnabled()
{
  return mCompositor->GetBackendType() != LayersBackend::LAYERS_BASIC &&
         mCompositor->SupportsEffect(EffectTypes::COMPONENT_ALPHA) &&
         LayerManager::AreComponentAlphaLayersEnabled();
}

bool
LayerManagerComposite::BeginTransaction()
{
  mInTransaction = true;

  if (!mCompositor->Ready()) {
    return false;
  }

  mIsCompositorReady = true;
  return true;
}

void
LayerManagerComposite::BeginTransactionWithDrawTarget(DrawTarget* aTarget, const IntRect& aRect)
{
  mInTransaction = true;

  if (!mCompositor->Ready()) {
    return;
  }

#ifdef MOZ_LAYERS_HAVE_LOG
  MOZ_LAYERS_LOG(("[----- BeginTransaction"));
  Log();
#endif

  if (mDestroyed) {
    NS_WARNING("Call on destroyed layer manager");
    return;
  }

  mIsCompositorReady = true;
  mCompositor->SetTargetContext(aTarget, aRect);
  mTarget = aTarget;
  mTargetBounds = aRect;
}

void
LayerManagerComposite::PostProcessLayers(nsIntRegion& aOpaqueRegion)
{
  LayerIntRegion visible;
  LayerComposite* rootComposite = static_cast<LayerComposite*>(mRoot->AsHostLayer());
  PostProcessLayers(mRoot, aOpaqueRegion, visible,
                    ViewAs<RenderTargetPixel>(rootComposite->GetShadowClipRect(),
                                              PixelCastJustification::RenderTargetIsParentLayerForRoot),
                    Nothing());
}

// We want to skip directly through ContainerLayers that don't have an intermediate
// surface. We compute occlusions for leaves and intermediate surfaces against
// the layer that they actually composite into so that we can use the final (snapped)
// effective transform.
bool ShouldProcessLayer(Layer* aLayer)
{
  if (!aLayer->AsContainerLayer()) {
    return true;
  }

  return aLayer->AsContainerLayer()->UseIntermediateSurface();
}

/**
 * Get accumulated transform of from the context creating layer to the
 * given layer.
 */
static Matrix4x4
GetAccTransformIn3DContext(Layer* aLayer) {
  Matrix4x4 transform = aLayer->GetLocalTransform();
  for (Layer* layer = aLayer->GetParent();
       layer && layer->Extend3DContext();
       layer = layer->GetParent()) {
    transform = transform * layer->GetLocalTransform();
  }
  return transform;
}

void
LayerManagerComposite::PostProcessLayers(Layer* aLayer,
                                         nsIntRegion& aOpaqueRegion,
                                         LayerIntRegion& aVisibleRegion,
                                         const Maybe<RenderTargetIntRect>& aRenderTargetClip,
                                         const Maybe<ParentLayerIntRect>& aClipFromAncestors)
{

  // Compute a clip that's the combination of our layer clip with the clip
  // from our ancestors.
  LayerComposite* composite = static_cast<LayerComposite*>(aLayer->AsHostLayer());
  Maybe<ParentLayerIntRect> layerClip = composite->GetShadowClipRect();
  MOZ_ASSERT(!layerClip || !aLayer->Combines3DTransformWithAncestors(),
             "The layer with a clip should not participate "
             "a 3D rendering context");
  Maybe<ParentLayerIntRect> outsideClip =
    IntersectMaybeRects(layerClip, aClipFromAncestors);

  Maybe<LayerIntRect> insideClip;
  if (aLayer->Extend3DContext()) {
    // If we're preserve-3d just pass the clip rect down directly, and we'll do the
    // conversion at the preserve-3d leaf Layer.
    if (outsideClip) {
      insideClip = Some(ViewAs<LayerPixel>(*outsideClip, PixelCastJustification::MovingDownToChildren));
    }
  } else if (outsideClip) {
    // Convert the combined clip into our pre-transform coordinate space, so
    // that it can later be intersected with our visible region.
    // If our transform is a perspective, there's no meaningful insideClip rect
    // we can compute (it would need to be a cone).
    Matrix4x4 localTransform = GetAccTransformIn3DContext(aLayer);
    if (!localTransform.HasPerspectiveComponent() && localTransform.Invert()) {
      LayerRect insideClipFloat =
        UntransformBy(ViewAs<ParentLayerToLayerMatrix4x4>(localTransform),
                      ParentLayerRect(*outsideClip),
                      LayerRect::MaxIntRect()).valueOr(LayerRect());
      insideClipFloat.RoundOut();
      LayerIntRect insideClipInt;
      if (insideClipFloat.ToIntRect(&insideClipInt)) {
        insideClip = Some(insideClipInt);
      }
    }
  }

  Maybe<ParentLayerIntRect> ancestorClipForChildren;
  if (insideClip) {
    ancestorClipForChildren =
      Some(ViewAs<ParentLayerPixel>(*insideClip, PixelCastJustification::MovingDownToChildren));
  }

  if (!ShouldProcessLayer(aLayer)) {
    MOZ_ASSERT(aLayer->AsContainerLayer() && !aLayer->AsContainerLayer()->UseIntermediateSurface());
    // For layers participating 3D rendering context, their visible
    // region should be empty (invisible), so we pass through them
    // without doing anything.
    for (Layer* child = aLayer->GetLastChild();
         child;
         child = child->GetPrevSibling()) {
      LayerComposite* childComposite = static_cast<LayerComposite*>(child->AsHostLayer());
      Maybe<RenderTargetIntRect> renderTargetClip = aRenderTargetClip;
      if (childComposite->GetShadowClipRect()) {
        RenderTargetIntRect clip = TransformBy(ViewAs<ParentLayerToRenderTargetMatrix4x4>(
          aLayer->GetEffectiveTransform(),
          PixelCastJustification::RenderTargetIsParentLayerForRoot),
                                               *childComposite->GetShadowClipRect());
        renderTargetClip = IntersectMaybeRects(renderTargetClip, Some(clip));
      }

      PostProcessLayers(child, aOpaqueRegion, aVisibleRegion,
                        renderTargetClip, ancestorClipForChildren);
    }
    return;
  }

  nsIntRegion localOpaque;
  // Treat layers on the path to the root of the 3D rendering context as
  // a giant layer if it is a leaf.
  Matrix4x4 transform = aLayer->GetEffectiveTransform();
  Matrix transform2d;
  Maybe<IntPoint> integerTranslation;
  // If aLayer has a simple transform (only an integer translation) then we
  // can easily convert aOpaqueRegion into pre-transform coordinates and include
  // that region.
  if (transform.Is2D(&transform2d)) {
    if (transform2d.IsIntegerTranslation()) {
      integerTranslation = Some(IntPoint::Truncate(transform2d.GetTranslation()));
      localOpaque = aOpaqueRegion;
      localOpaque.MoveBy(-*integerTranslation);
    }
  }

  // Save the value of localOpaque, which currently stores the region obscured
  // by siblings (and uncles and such), before our descendants contribute to it.
  nsIntRegion obscured = localOpaque;

  // Recurse on our descendants, in front-to-back order. In this process:
  //  - Occlusions are computed for them, and they contribute to localOpaque.
  //  - They recalculate their visible regions, taking ancestorClipForChildren
  //    into account, and accumulate them into descendantsVisibleRegion.
  LayerIntRegion descendantsVisibleRegion;

  bool hasPreserve3DChild = false;
  for (Layer* child = aLayer->GetLastChild(); child; child = child->GetPrevSibling()) {
    MOZ_ASSERT(aLayer->AsContainerLayer()->UseIntermediateSurface());
    LayerComposite* childComposite = static_cast<LayerComposite*>(child->AsHostLayer());
    PostProcessLayers(child, localOpaque, descendantsVisibleRegion,
                      ViewAs<RenderTargetPixel>(childComposite->GetShadowClipRect(),
                                                PixelCastJustification::RenderTargetIsParentLayerForRoot),
                      ancestorClipForChildren);
    if (child->Extend3DContext()) {
      hasPreserve3DChild = true;
    }
  }

  // Recalculate our visible region.
  LayerIntRegion visible = composite->GetShadowVisibleRegion();

  // If we have descendants, throw away the visible region stored on this
  // layer, and use the region accumulated by our descendants instead.
  if (aLayer->GetFirstChild() && !hasPreserve3DChild) {
    visible = descendantsVisibleRegion;
  }

  // Subtract any areas that we know to be opaque.
  if (!obscured.IsEmpty()) {
    visible.SubOut(LayerIntRegion::FromUnknownRegion(obscured));
  }

  // Clip the visible region using the combined clip.
  if (insideClip) {
    visible.AndWith(*insideClip);
  }
  composite->SetShadowVisibleRegion(visible);

  // Transform the newly calculated visible region into our parent's space,
  // apply our clip to it (if any), and accumulate it into |aVisibleRegion|
  // for the caller to use.
  ParentLayerIntRegion visibleParentSpace = TransformBy(
      ViewAs<LayerToParentLayerMatrix4x4>(transform), visible);
  aVisibleRegion.OrWith(ViewAs<LayerPixel>(visibleParentSpace,
      PixelCastJustification::MovingDownToChildren));

  // If we have a simple transform, then we can add our opaque area into
  // aOpaqueRegion.
  if (integerTranslation &&
      !aLayer->HasMaskLayers() &&
      aLayer->IsOpaqueForVisibility()) {
    if (aLayer->IsOpaque()) {
      localOpaque.OrWith(composite->GetFullyRenderedRegion());
    }
    localOpaque.MoveBy(*integerTranslation);
    if (aRenderTargetClip) {
      localOpaque.AndWith(aRenderTargetClip->ToUnknownRect());
    }
    aOpaqueRegion.OrWith(localOpaque);
  }
}

void
LayerManagerComposite::EndTransaction(const TimeStamp& aTimeStamp,
                                      EndTransactionFlags aFlags)
{
  NS_ASSERTION(mInTransaction, "Didn't call BeginTransaction?");
  NS_ASSERTION(!(aFlags & END_NO_COMPOSITE),
               "Shouldn't get END_NO_COMPOSITE here");
  mInTransaction = false;
  mRenderStartTime = TimeStamp::Now();

  if (!mIsCompositorReady) {
    return;
  }
  mIsCompositorReady = false;

#ifdef MOZ_LAYERS_HAVE_LOG
  MOZ_LAYERS_LOG(("  ----- (beginning paint)"));
  Log();
#endif

  if (mDestroyed) {
    NS_WARNING("Call on destroyed layer manager");
    return;
  }

  // Set composition timestamp here because we need it in
  // ComputeEffectiveTransforms (so the correct video frame size is picked) and
  // also to compute invalid regions properly.
  SetCompositionTime(aTimeStamp);

  if (mRoot && !(aFlags & END_NO_IMMEDIATE_REDRAW)) {
    MOZ_ASSERT(!aTimeStamp.IsNull());
    UpdateAndRender();
    mCompositor->FlushPendingNotifyNotUsed();
  }

  mCompositor->ClearTargetContext();
  mTarget = nullptr;

#ifdef MOZ_LAYERS_HAVE_LOG
  Log();
  MOZ_LAYERS_LOG(("]----- EndTransaction"));
#endif
}

void
LayerManagerComposite::UpdateAndRender()
{
  nsIntRegion invalid;
  // The results of our drawing always go directly into a pixel buffer,
  // so we don't need to pass any global transform here.
  mRoot->ComputeEffectiveTransforms(gfx::Matrix4x4());

  nsIntRegion opaque;
  PostProcessLayers(opaque);

  if (mClonedLayerTreeProperties) {
    // We need to compute layer tree differences even if we're not going to
    // immediately use the resulting damage area, since ComputeDifferences
    // is also responsible for invalidates intermediate surfaces in
    // ContainerLayers.
    nsIntRegion changed = mClonedLayerTreeProperties->ComputeDifferences(mRoot, nullptr);

    if (mTarget) {
      // Since we're composing to an external target, we're not going to use
      // the damage region from layers changes - we want to composite
      // everything in the target bounds. Instead we accumulate the layers
      // damage region for the next window composite.
      mInvalidRegion.Or(mInvalidRegion, changed);
    } else {
      invalid = Move(changed);
    }
  }

  if (mTarget) {
    invalid.Or(invalid, mTargetBounds);
  } else {
    // If we didn't have a previous layer tree, invalidate the entire render
    // area.
    if (!mClonedLayerTreeProperties) {
      invalid.Or(invalid, mRenderBounds);
    }

    // Add any additional invalid rects from the window manager or previous
    // damage computed during ComposeToTarget().
    invalid.Or(invalid, mInvalidRegion);
    mInvalidRegion.SetEmpty();
  }

  if (invalid.IsEmpty() && !mWindowOverlayChanged) {
    // Composition requested, but nothing has changed. Don't do any work.
    mClonedLayerTreeProperties = LayerProperties::CloneFrom(GetRoot());
    return;
  }

  // We don't want our debug overlay to cause more frames to happen
  // so we will invalidate after we've decided if something changed.
  InvalidateDebugOverlay(invalid, mRenderBounds);

  Render(invalid, opaque);
#if defined(MOZ_WIDGET_ANDROID)
  RenderToPresentationSurface();
#endif
  mWindowOverlayChanged = false;

  // Update cached layer tree information.
  mClonedLayerTreeProperties = LayerProperties::CloneFrom(GetRoot());
}

already_AddRefed<DrawTarget>
LayerManagerComposite::CreateOptimalMaskDrawTarget(const IntSize &aSize)
{
  MOZ_CRASH("Should only be called on the drawing side");
  return nullptr;
}

LayerComposite*
LayerManagerComposite::RootLayer() const
{
  if (mDestroyed) {
    NS_WARNING("Call on destroyed layer manager");
    return nullptr;
  }

  return ToLayerComposite(mRoot);
}

void
LayerManagerComposite::InvalidateDebugOverlay(nsIntRegion& aInvalidRegion, const IntRect& aBounds)
{
  bool drawFps = gfxPrefs::LayersDrawFPS();
  bool drawFrameColorBars = gfxPrefs::CompositorDrawColorBars();

  if (drawFps) {
    aInvalidRegion.Or(aInvalidRegion, nsIntRect(0, 0, 650, 400));
  }
  if (drawFrameColorBars) {
    aInvalidRegion.Or(aInvalidRegion, nsIntRect(0, 0, 10, aBounds.height));
  }

#ifdef USE_SKIA
  bool drawPaintTimes = gfxPrefs::AlwaysPaint();
  if (drawPaintTimes) {
    aInvalidRegion.Or(aInvalidRegion, nsIntRect(PaintCounter::GetPaintRect()));
  }
#endif
}

#ifdef USE_SKIA
void
LayerManagerComposite::DrawPaintTimes(Compositor* aCompositor)
{
  if (!mPaintCounter) {
    mPaintCounter = new PaintCounter();
  }

  TimeDuration compositeTime = TimeStamp::Now() - mRenderStartTime;
  mPaintCounter->Draw(aCompositor, mLastPaintTime, compositeTime);
}
#endif

static uint16_t sFrameCount = 0;
void
LayerManagerComposite::RenderDebugOverlay(const IntRect& aBounds)
{
  bool drawFps = gfxPrefs::LayersDrawFPS();
  bool drawFrameColorBars = gfxPrefs::CompositorDrawColorBars();

  if (drawFps) {
    float alpha = 1;
#ifdef ANDROID
    // Draw a translation delay warning overlay
    int width;
    int border;

    TimeStamp now = TimeStamp::Now();
    if (!mWarnTime.IsNull() && (now - mWarnTime).ToMilliseconds() < kVisualWarningDuration) {
      EffectChain effects;

      // Black blorder
      border = 4;
      width = 6;
      effects.mPrimaryEffect = new EffectSolidColor(gfx::Color(0, 0, 0, 1));
      mCompositor->DrawQuad(gfx::Rect(border, border, aBounds.width - 2 * border, width),
                            aBounds, effects, alpha, gfx::Matrix4x4());
      mCompositor->DrawQuad(gfx::Rect(border, aBounds.height - border - width, aBounds.width - 2 * border, width),
                            aBounds, effects, alpha, gfx::Matrix4x4());
      mCompositor->DrawQuad(gfx::Rect(border, border + width, width, aBounds.height - 2 * border - width * 2),
                            aBounds, effects, alpha, gfx::Matrix4x4());
      mCompositor->DrawQuad(gfx::Rect(aBounds.width - border - width, border + width, width, aBounds.height - 2 * border - 2 * width),
                            aBounds, effects, alpha, gfx::Matrix4x4());

      // Content
      border = 5;
      width = 4;
      effects.mPrimaryEffect = new EffectSolidColor(gfx::Color(1, 1.f - mWarningLevel, 0, 1));
      mCompositor->DrawQuad(gfx::Rect(border, border, aBounds.width - 2 * border, width),
                            aBounds, effects, alpha, gfx::Matrix4x4());
      mCompositor->DrawQuad(gfx::Rect(border, aBounds.height - border - width, aBounds.width - 2 * border, width),
                            aBounds, effects, alpha, gfx::Matrix4x4());
      mCompositor->DrawQuad(gfx::Rect(border, border + width, width, aBounds.height - 2 * border - width * 2),
                            aBounds, effects, alpha, gfx::Matrix4x4());
      mCompositor->DrawQuad(gfx::Rect(aBounds.width - border - width, border + width, width, aBounds.height - 2 * border - 2 * width),
                            aBounds, effects, alpha, gfx::Matrix4x4());
      SetDebugOverlayWantsNextFrame(true);
    }
#endif

    GPUStats stats;
    stats.mScreenPixels = mRenderBounds.width * mRenderBounds.height;
    mCompositor->GetFrameStats(&stats);

    std::string text = mDiagnostics->GetFrameOverlayString(stats);
    mTextRenderer->RenderText(
      mCompositor,
      text,
      IntPoint(2, 5),
      Matrix4x4(),
      24,
      600,
      TextRenderer::FontType::FixedWidth);

    if (mUnusedApzTransformWarning) {
      // If we have an unused APZ transform on this composite, draw a 20x20 red box
      // in the top-right corner
      EffectChain effects;
      effects.mPrimaryEffect = new EffectSolidColor(gfx::Color(1, 0, 0, 1));
      mCompositor->DrawQuad(gfx::Rect(aBounds.width - 20, 0, 20, 20),
                            aBounds, effects, alpha, gfx::Matrix4x4());

      mUnusedApzTransformWarning = false;
      SetDebugOverlayWantsNextFrame(true);
    }
    if (mDisabledApzWarning) {
      // If we have a disabled APZ on this composite, draw a 20x20 yellow box
      // in the top-right corner, to the left of the unused-apz-transform
      // warning box
      EffectChain effects;
      effects.mPrimaryEffect = new EffectSolidColor(gfx::Color(1, 1, 0, 1));
      mCompositor->DrawQuad(gfx::Rect(aBounds.width - 40, 0, 20, 20),
                            aBounds, effects, alpha, gfx::Matrix4x4());

      mDisabledApzWarning = false;
      SetDebugOverlayWantsNextFrame(true);
    }
  }

  if (drawFrameColorBars) {
    gfx::IntRect sideRect(0, 0, 10, aBounds.height);

    EffectChain effects;
    effects.mPrimaryEffect = new EffectSolidColor(gfxUtils::GetColorForFrameNumber(sFrameCount));
    mCompositor->DrawQuad(Rect(sideRect),
                          sideRect,
                          effects,
                          1.0,
                          gfx::Matrix4x4());

  }

  if (drawFrameColorBars) {
    // We intentionally overflow at 2^16.
    sFrameCount++;
  }

#ifdef USE_SKIA
  bool drawPaintTimes = gfxPrefs::AlwaysPaint();
  if (drawPaintTimes) {
    DrawPaintTimes(mCompositor);
  }
#endif
}

RefPtr<CompositingRenderTarget>
LayerManagerComposite::PushGroupForLayerEffects()
{
  // This is currently true, so just making sure that any new use of this
  // method is flagged for investigation
  MOZ_ASSERT(gfxPrefs::LayersEffectInvert() ||
             gfxPrefs::LayersEffectGrayscale() ||
             gfxPrefs::LayersEffectContrast() != 0.0);

  RefPtr<CompositingRenderTarget> previousTarget = mCompositor->GetCurrentRenderTarget();
  // make our render target the same size as the destination target
  // so that we don't have to change size if the drawing area changes.
  IntRect rect(previousTarget->GetOrigin(), previousTarget->GetSize());
  // XXX: I'm not sure if this is true or not...
  MOZ_ASSERT(rect.x == 0 && rect.y == 0);
  if (!mTwoPassTmpTarget ||
      mTwoPassTmpTarget->GetSize() != previousTarget->GetSize() ||
      mTwoPassTmpTarget->GetOrigin() != previousTarget->GetOrigin()) {
    mTwoPassTmpTarget = mCompositor->CreateRenderTarget(rect, INIT_MODE_NONE);
  }
  MOZ_ASSERT(mTwoPassTmpTarget);
  mCompositor->SetRenderTarget(mTwoPassTmpTarget);
  return previousTarget;
}
void
LayerManagerComposite::PopGroupForLayerEffects(RefPtr<CompositingRenderTarget> aPreviousTarget,
                                               IntRect aClipRect,
                                               bool aGrayscaleEffect,
                                               bool aInvertEffect,
                                               float aContrastEffect)
{
  MOZ_ASSERT(mTwoPassTmpTarget);

  // This is currently true, so just making sure that any new use of this
  // method is flagged for investigation
  MOZ_ASSERT(aInvertEffect || aGrayscaleEffect || aContrastEffect != 0.0);

  mCompositor->SetRenderTarget(aPreviousTarget);

  EffectChain effectChain(RootLayer());
  Matrix5x4 effectMatrix;
  if (aGrayscaleEffect) {
    // R' = G' = B' = luminance
    // R' = 0.2126*R + 0.7152*G + 0.0722*B
    // G' = 0.2126*R + 0.7152*G + 0.0722*B
    // B' = 0.2126*R + 0.7152*G + 0.0722*B
    Matrix5x4 grayscaleMatrix(0.2126f, 0.2126f, 0.2126f, 0,
                              0.7152f, 0.7152f, 0.7152f, 0,
                              0.0722f, 0.0722f, 0.0722f, 0,
                              0,       0,       0,       1,
                              0,       0,       0,       0);
    effectMatrix = grayscaleMatrix;
  }

  if (aInvertEffect) {
    // R' = 1 - R
    // G' = 1 - G
    // B' = 1 - B
    Matrix5x4 colorInvertMatrix(-1,  0,  0, 0,
                                 0, -1,  0, 0,
                                 0,  0, -1, 0,
                                 0,  0,  0, 1,
                                 1,  1,  1, 0);
    effectMatrix = effectMatrix * colorInvertMatrix;
  }

  if (aContrastEffect != 0.0) {
    // Multiplying with:
    // R' = (1 + c) * (R - 0.5) + 0.5
    // G' = (1 + c) * (G - 0.5) + 0.5
    // B' = (1 + c) * (B - 0.5) + 0.5
    float cP1 = aContrastEffect + 1;
    float hc = 0.5*aContrastEffect;
    Matrix5x4 contrastMatrix( cP1,   0,   0, 0,
                                0, cP1,   0, 0,
                                0,   0, cP1, 0,
                                0,   0,   0, 1,
                              -hc, -hc, -hc, 0);
    effectMatrix = effectMatrix * contrastMatrix;
  }

  effectChain.mPrimaryEffect = new EffectRenderTarget(mTwoPassTmpTarget);
  effectChain.mSecondaryEffects[EffectTypes::COLOR_MATRIX] = new EffectColorMatrix(effectMatrix);

  mCompositor->DrawQuad(Rect(Point(0, 0), Size(mTwoPassTmpTarget->GetSize())), aClipRect, effectChain, 1.,
                        Matrix4x4());
}

// Used to clear the 'mLayerComposited' flag at the beginning of each Render().
static void
ClearLayerFlags(Layer* aLayer) {
  ForEachNode<ForwardIterator>(
      aLayer,
      [] (Layer* layer)
      {
        if (layer->AsHostLayer()) {
          static_cast<LayerComposite*>(layer->AsHostLayer())->SetLayerComposited(false);
        }
      });
}

#if defined(MOZ_WIDGET_ANDROID)
class ScopedCompositorRenderOffset {
public:
  ScopedCompositorRenderOffset(CompositorOGL* aCompositor, const ScreenPoint& aOffset) :
    mCompositor(aCompositor),
    mOriginalOffset(mCompositor->GetScreenRenderOffset()),
    mOriginalProjection(mCompositor->GetProjMatrix())
  {
    ScreenPoint offset(mOriginalOffset.x + aOffset.x, mOriginalOffset.y + aOffset.y);
    mCompositor->SetScreenRenderOffset(offset);
    // Calling CompositorOGL::SetScreenRenderOffset does not affect the projection matrix
    // so adjust that as well.
    gfx::Matrix4x4 mat = mOriginalProjection;
    mat.PreTranslate(aOffset.x, aOffset.y, 0.0f);
    mCompositor->SetProjMatrix(mat);
  }
  ~ScopedCompositorRenderOffset()
  {
    mCompositor->SetScreenRenderOffset(mOriginalOffset);
    mCompositor->SetProjMatrix(mOriginalProjection);
  }
private:
  CompositorOGL* const mCompositor;
  const ScreenPoint mOriginalOffset;
  const gfx::Matrix4x4 mOriginalProjection;
};
#endif // defined(MOZ_WIDGET_ANDROID)

void
LayerManagerComposite::Render(const nsIntRegion& aInvalidRegion, const nsIntRegion& aOpaqueRegion)
{
  AUTO_PROFILER_LABEL("LayerManagerComposite::Render", GRAPHICS);

  if (mDestroyed || !mCompositor || mCompositor->IsDestroyed()) {
    NS_WARNING("Call on destroyed layer manager");
    return;
  }

  ClearLayerFlags(mRoot);

  // At this time, it doesn't really matter if these preferences change
  // during the execution of the function; we should be safe in all
  // permutations. However, may as well just get the values onces and
  // then use them, just in case the consistency becomes important in
  // the future.
  bool invertVal = gfxPrefs::LayersEffectInvert();
  bool grayscaleVal = gfxPrefs::LayersEffectGrayscale();
  float contrastVal = gfxPrefs::LayersEffectContrast();
  bool haveLayerEffects = (invertVal || grayscaleVal || contrastVal != 0.0);

  // Set LayerScope begin/end frame
  LayerScopeAutoFrame frame(PR_Now());

  // Dump to console
  if (gfxPrefs::LayersDump()) {
    this->Dump(/* aSorted= */true);
  } else if (profiler_feature_active(ProfilerFeature::LayersDump)) {
    std::stringstream ss;
    Dump(ss);
    profiler_tracing("log", ss.str().c_str());
  }

  // Dump to LayerScope Viewer
  if (LayerScope::CheckSendable()) {
    // Create a LayersPacket, dump Layers into it and transfer the
    // packet('s ownership) to LayerScope.
    auto packet = MakeUnique<layerscope::Packet>();
    layerscope::LayersPacket* layersPacket = packet->mutable_layers();
    this->Dump(layersPacket);
    LayerScope::SendLayerDump(Move(packet));
  }

  mozilla::widget::WidgetRenderingContext widgetContext;
#if defined(XP_MACOSX)
  widgetContext.mLayerManager = this;
#elif defined(MOZ_WIDGET_ANDROID)
  widgetContext.mCompositor = GetCompositor();
#endif

  {
    AUTO_PROFILER_LABEL("LayerManagerComposite::Render:Prerender", GRAPHICS);

    if (!mCompositor->GetWidget()->PreRender(&widgetContext)) {
      return;
    }
  }

  ParentLayerIntRect clipRect;
  IntRect bounds(mRenderBounds.x, mRenderBounds.y, mRenderBounds.width, mRenderBounds.height);
  IntRect actualBounds;

  CompositorBench(mCompositor, bounds);

  MOZ_ASSERT(mRoot->GetOpacity() == 1);
#if defined(MOZ_WIDGET_ANDROID)
  LayerMetricsWrapper wrapper = GetRootContentLayer();
  if (wrapper) {
    mCompositor->SetClearColor(wrapper.Metadata().GetBackgroundColor());
  } else {
    mCompositor->SetClearColorToDefault();
  }
#endif
  if (mRoot->GetClipRect()) {
    clipRect = *mRoot->GetClipRect();
    IntRect rect(clipRect.x, clipRect.y, clipRect.width, clipRect.height);
    mCompositor->BeginFrame(aInvalidRegion, &rect, bounds, aOpaqueRegion, nullptr, &actualBounds);
  } else {
    gfx::IntRect rect;
    mCompositor->BeginFrame(aInvalidRegion, nullptr, bounds, aOpaqueRegion, &rect, &actualBounds);
    clipRect = ParentLayerIntRect(rect.x, rect.y, rect.width, rect.height);
  }
#if defined(MOZ_WIDGET_ANDROID)
  ScreenCoord offset = GetContentShiftForToolbar();
  ScopedCompositorRenderOffset scopedOffset(mCompositor->AsCompositorOGL(), ScreenPoint(0.0f, offset));
#endif

  if (actualBounds.IsEmpty()) {
    mCompositor->GetWidget()->PostRender(&widgetContext);
    return;
  }

  // Allow widget to render a custom background.
  mCompositor->GetWidget()->DrawWindowUnderlay(
    &widgetContext, LayoutDeviceIntRect::FromUnknownRect(actualBounds));

  RefPtr<CompositingRenderTarget> previousTarget;
  if (haveLayerEffects) {
    previousTarget = PushGroupForLayerEffects();
  } else {
    mTwoPassTmpTarget = nullptr;
  }

  // Render our layers.
  {
    Diagnostics::Record record(mRenderStartTime);
    RootLayer()->Prepare(ViewAs<RenderTargetPixel>(clipRect, PixelCastJustification::RenderTargetIsParentLayerForRoot));
    if (record.Recording()) {
      mDiagnostics->RecordPrepareTime(record.Duration());
    }
  }
  // Execute draw commands.
  {
    Diagnostics::Record record;
    RootLayer()->RenderLayer(clipRect.ToUnknownRect(), Nothing());
    if (record.Recording()) {
      mDiagnostics->RecordCompositeTime(record.Duration());
    }
  }
  RootLayer()->Cleanup();

  if (!mRegionToClear.IsEmpty()) {
    for (auto iter = mRegionToClear.RectIter(); !iter.Done(); iter.Next()) {
      const IntRect& r = iter.Get();
      mCompositor->ClearRect(Rect(r.x, r.y, r.width, r.height));
    }
  }

  if (mTwoPassTmpTarget) {
    MOZ_ASSERT(haveLayerEffects);
    PopGroupForLayerEffects(previousTarget, clipRect.ToUnknownRect(),
                            grayscaleVal, invertVal, contrastVal);
  }

  // Allow widget to render a custom foreground.
  mCompositor->GetWidget()->DrawWindowOverlay(
    &widgetContext, LayoutDeviceIntRect::FromUnknownRect(actualBounds));

  mCompositor->NormalDrawingDone();

#if defined(MOZ_WIDGET_ANDROID)
  // Depending on the content shift the toolbar may be rendered on top of
  // some of the content so it must be rendered after the content.
  RenderToolbar();
  HandlePixelsTarget();
#endif // defined(MOZ_WIDGET_ANDROID)

  // Debugging
  RenderDebugOverlay(actualBounds);

  {
    AUTO_PROFILER_LABEL("LayerManagerComposite::Render:EndFrame", GRAPHICS);

    mCompositor->EndFrame();

    // Call after EndFrame()
    mCompositor->SetDispAcquireFence(mRoot);
  }

  mCompositor->GetWidget()->PostRender(&widgetContext);

  RecordFrame();
}

#if defined(MOZ_WIDGET_ANDROID)
class ScopedCompositorProjMatrix {
public:
  ScopedCompositorProjMatrix(CompositorOGL* aCompositor, const Matrix4x4& aProjMatrix):
    mCompositor(aCompositor),
    mOriginalProjMatrix(mCompositor->GetProjMatrix())
  {
    mCompositor->SetProjMatrix(aProjMatrix);
  }

  ~ScopedCompositorProjMatrix()
  {
    mCompositor->SetProjMatrix(mOriginalProjMatrix);
  }
private:
  CompositorOGL* const mCompositor;
  const Matrix4x4 mOriginalProjMatrix;
};

class ScopedCompostitorSurfaceSize {
public:
  ScopedCompostitorSurfaceSize(CompositorOGL* aCompositor, const gfx::IntSize& aSize) :
    mCompositor(aCompositor),
    mOriginalSize(mCompositor->GetDestinationSurfaceSize())
  {
    mCompositor->SetDestinationSurfaceSize(aSize);
  }
  ~ScopedCompostitorSurfaceSize()
  {
    mCompositor->SetDestinationSurfaceSize(mOriginalSize);
  }
private:
  CompositorOGL* const mCompositor;
  const gfx::IntSize mOriginalSize;
};

class ScopedContextSurfaceOverride {
public:
  ScopedContextSurfaceOverride(GLContextEGL* aContext, void* aSurface) :
    mContext(aContext)
  {
    MOZ_ASSERT(aSurface);
    mContext->SetEGLSurfaceOverride(aSurface);
    mContext->MakeCurrent(true);
  }
  ~ScopedContextSurfaceOverride()
  {
    mContext->SetEGLSurfaceOverride(EGL_NO_SURFACE);
    mContext->MakeCurrent(true);
  }
private:
  GLContextEGL* const mContext;
};

void
LayerManagerComposite::RenderToPresentationSurface()
{
  widget::CompositorWidget* const widget = mCompositor->GetWidget();
  ANativeWindow* window = widget->AsAndroid()->GetPresentationANativeWindow();

  if (!window) {
    return;
  }

  EGLSurface surface = widget->AsAndroid()->GetPresentationEGLSurface();

  if (!surface) {
    //create surface;
    surface = GLContextProviderEGL::CreateEGLSurface(window);
    if (!surface) {
      return;
    }

    widget->AsAndroid()->SetPresentationEGLSurface(surface);
  }

  CompositorOGL* compositor = mCompositor->AsCompositorOGL();
  GLContext* gl = compositor->gl();
  GLContextEGL* egl = GLContextEGL::Cast(gl);

  if (!egl) {
    return;
  }

  const IntSize windowSize(ANativeWindow_getWidth(window),
                           ANativeWindow_getHeight(window));


  if ((windowSize.width <= 0) || (windowSize.height <= 0)) {
    return;
  }

  ScreenRotation rotation = compositor->GetScreenRotation();

  const int actualWidth = windowSize.width;
  const int actualHeight = windowSize.height;

  const gfx::IntSize originalSize = compositor->GetDestinationSurfaceSize();
  const nsIntRect originalRect = nsIntRect(0, 0, originalSize.width, originalSize.height);

  int pageWidth = originalSize.width;
  int pageHeight = originalSize.height;
  if (rotation == ROTATION_90 || rotation == ROTATION_270) {
    pageWidth = originalSize.height;
    pageHeight = originalSize.width;
  }

  float scale = 1.0;

  if ((pageWidth > actualWidth) || (pageHeight > actualHeight)) {
    const float scaleWidth = (float)actualWidth / (float)pageWidth;
    const float scaleHeight = (float)actualHeight / (float)pageHeight;
    scale = scaleWidth <= scaleHeight ? scaleWidth : scaleHeight;
  }

  const gfx::IntSize actualSize(actualWidth, actualHeight);
  ScopedCompostitorSurfaceSize overrideSurfaceSize(compositor, actualSize);

  const ScreenPoint offset((actualWidth - (int)(scale * pageWidth)) / 2, 0);
  ScopedContextSurfaceOverride overrideSurface(egl, surface);

  Matrix viewMatrix = ComputeTransformForRotation(originalRect,
                                                  rotation);
  viewMatrix.Invert(); // unrotate
  viewMatrix.PostScale(scale, scale);
  viewMatrix.PostTranslate(offset.x, offset.y);
  Matrix4x4 matrix = Matrix4x4::From2D(viewMatrix);

  mRoot->ComputeEffectiveTransforms(matrix);
  nsIntRegion opaque;
  PostProcessLayers(opaque);

  nsIntRegion invalid;
  IntRect bounds = IntRect::Truncate(0, 0, scale * pageWidth, actualHeight);
  IntRect rect, actualBounds;
  MOZ_ASSERT(mRoot->GetOpacity() == 1);
  mCompositor->BeginFrame(invalid, nullptr, bounds, nsIntRegion(), &rect, &actualBounds);

  // The Java side of Fennec sets a scissor rect that accounts for
  // chrome such as the URL bar. Override that so that the entire frame buffer
  // is cleared.
  ScopedScissorRect scissorRect(egl, 0, 0, actualWidth, actualHeight);
  egl->fClearColor(0.0, 0.0, 0.0, 0.0);
  egl->fClear(LOCAL_GL_COLOR_BUFFER_BIT);

  const IntRect clipRect = IntRect::Truncate(0, 0, actualWidth, actualHeight);

  RootLayer()->Prepare(RenderTargetIntRect::FromUnknownRect(clipRect));
  RootLayer()->RenderLayer(clipRect, Nothing());

  mCompositor->EndFrame();
}

ScreenCoord
LayerManagerComposite::GetContentShiftForToolbar()
{
  ScreenCoord result(0.0f);
  // If GetTargetContext return is not null we are not drawing to the screen so there will not be any content offset.
  if (mCompositor->GetTargetContext() != nullptr) {
    return result;
  }

  if (CompositorBridgeParent* bridge = mCompositor->GetCompositorBridgeParent()) {
    AndroidDynamicToolbarAnimator* animator = bridge->GetAPZCTreeManager()->GetAndroidDynamicToolbarAnimator();
    MOZ_RELEASE_ASSERT(animator);
    result.value = (float)animator->GetCurrentContentOffset().value;
  }
  return result;
}

void
LayerManagerComposite::RenderToolbar()
{
  // If GetTargetContext return is not null we are not drawing to the screen so don't draw the toolbar.
  if (mCompositor->GetTargetContext() != nullptr) {
    return;
  }

  if (CompositorBridgeParent* bridge = mCompositor->GetCompositorBridgeParent()) {
    AndroidDynamicToolbarAnimator* animator = bridge->GetAPZCTreeManager()->GetAndroidDynamicToolbarAnimator();
    MOZ_RELEASE_ASSERT(animator);

    int32_t toolbarHeight = animator->GetCurrentToolbarHeight();
    if (toolbarHeight == 0) {
      return;
    }

    EffectChain effects;
    effects.mPrimaryEffect = animator->GetToolbarEffect(mCompositor->AsCompositorOGL());
    // If GetToolbarEffect returns null, nothing is rendered for the static snapshot of the toolbar.
    // If the real toolbar chrome is not covering this portion of the surface, the clear color
    // of the surface will be visible. On Android the clear color is the background color of the page.
    if (effects.mPrimaryEffect) {
      ScopedCompositorRenderOffset toolbarOffset(mCompositor->AsCompositorOGL(),
                                                 ScreenPoint(0.0f, -animator->GetCurrentContentOffset()));
      mCompositor->DrawQuad(gfx::Rect(0, 0, mRenderBounds.width, toolbarHeight),
                            IntRect(0, 0, mRenderBounds.width, toolbarHeight), effects, 1.0, gfx::Matrix4x4());
    }
  }
}

// Used by robocop tests to get a snapshot of the frame buffer.
void
LayerManagerComposite::HandlePixelsTarget()
{
  if (!mScreenPixelsTarget) {
    return;
  }

  int32_t bufferWidth = mRenderBounds.width;
  int32_t bufferHeight = mRenderBounds.height;
  ipc::Shmem mem;
  if (!mScreenPixelsTarget->AllocPixelBuffer(bufferWidth * bufferHeight * sizeof(uint32_t), &mem)) {
    // Failed to alloc shmem, Just bail out.
    return;
  }
  CompositorOGL* compositor = mCompositor->AsCompositorOGL();
  GLContext* gl = compositor->gl();
  MOZ_ASSERT(gl);
  gl->fReadPixels(0, 0, bufferWidth, bufferHeight, LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, mem.get<uint8_t>());
  Unused << mScreenPixelsTarget->SendScreenPixels(mem, ScreenIntSize(bufferWidth, bufferHeight));
  mScreenPixelsTarget = nullptr;
}
#endif

class TextLayerComposite : public TextLayer,
                           public LayerComposite
{
public:
  explicit TextLayerComposite(LayerManagerComposite *aManager)
    : TextLayer(aManager, nullptr)
    , LayerComposite(aManager)
  {
    MOZ_COUNT_CTOR(TextLayerComposite);
    mImplData = static_cast<LayerComposite*>(this);
  }

protected:
  ~TextLayerComposite()
  {
    MOZ_COUNT_DTOR(TextLayerComposite);
    Destroy();
  }

public:
  // LayerComposite Implementation
  virtual Layer* GetLayer() override { return this; }

  virtual void SetLayerManager(HostLayerManager* aManager) override
  {
    LayerComposite::SetLayerManager(aManager);
    mManager = aManager;
  }

  virtual void Destroy() override { mDestroyed = true; }

  virtual void RenderLayer(const gfx::IntRect& aClipRect,
                           const Maybe<gfx::Polygon>& aGeometry) override {}
  virtual void CleanupResources() override {};

  virtual void GenEffectChain(EffectChain& aEffect) override {}

  CompositableHost* GetCompositableHost() override { return nullptr; }

  virtual HostLayer* AsHostLayer() override { return this; }

  virtual const char* Name() const override { return "TextLayerComposite"; }
};

class BorderLayerComposite : public BorderLayer,
                             public LayerComposite
{
public:
  explicit BorderLayerComposite(LayerManagerComposite *aManager)
    : BorderLayer(aManager, nullptr)
    , LayerComposite(aManager)
  {
    MOZ_COUNT_CTOR(BorderLayerComposite);
    mImplData = static_cast<LayerComposite*>(this);
  }

protected:
  ~BorderLayerComposite()
  {
    MOZ_COUNT_DTOR(BorderLayerComposite);
    Destroy();
  }

public:
  // LayerComposite Implementation
  virtual Layer* GetLayer() override { return this; }

  virtual void SetLayerManager(HostLayerManager* aManager) override
  {
    LayerComposite::SetLayerManager(aManager);
    mManager = aManager;
  }

  virtual void Destroy() override { mDestroyed = true; }

  virtual void RenderLayer(const gfx::IntRect& aClipRect,
                           const Maybe<gfx::Polygon>& aGeometry) override {}
  virtual void CleanupResources() override {};

  virtual void GenEffectChain(EffectChain& aEffect) override {}

  CompositableHost* GetCompositableHost() override { return nullptr; }

  virtual HostLayer* AsHostLayer() override { return this; }

  virtual const char* Name() const override { return "BorderLayerComposite"; }
};

already_AddRefed<PaintedLayer>
LayerManagerComposite::CreatePaintedLayer()
{
  if (mDestroyed) {
    NS_WARNING("Call on destroyed layer manager");
    return nullptr;
  }
  return RefPtr<PaintedLayer>(new PaintedLayerComposite(this)).forget();
}

already_AddRefed<ContainerLayer>
LayerManagerComposite::CreateContainerLayer()
{
  if (mDestroyed) {
    NS_WARNING("Call on destroyed layer manager");
    return nullptr;
  }
  return RefPtr<ContainerLayer>(new ContainerLayerComposite(this)).forget();
}

already_AddRefed<ImageLayer>
LayerManagerComposite::CreateImageLayer()
{
  if (mDestroyed) {
    NS_WARNING("Call on destroyed layer manager");
    return nullptr;
  }
  return RefPtr<ImageLayer>(new ImageLayerComposite(this)).forget();
}

already_AddRefed<ColorLayer>
LayerManagerComposite::CreateColorLayer()
{
  if (LayerManagerComposite::mDestroyed) {
    NS_WARNING("Call on destroyed layer manager");
    return nullptr;
  }
  return RefPtr<ColorLayer>(new ColorLayerComposite(this)).forget();
}

already_AddRefed<CanvasLayer>
LayerManagerComposite::CreateCanvasLayer()
{
  if (LayerManagerComposite::mDestroyed) {
    NS_WARNING("Call on destroyed layer manager");
    return nullptr;
  }
  return RefPtr<CanvasLayer>(new CanvasLayerComposite(this)).forget();
}

already_AddRefed<RefLayer>
LayerManagerComposite::CreateRefLayer()
{
  if (LayerManagerComposite::mDestroyed) {
    NS_WARNING("Call on destroyed layer manager");
    return nullptr;
  }
  return RefPtr<RefLayer>(new RefLayerComposite(this)).forget();
}

already_AddRefed<TextLayer>
LayerManagerComposite::CreateTextLayer()
{
  if (LayerManagerComposite::mDestroyed) {
    NS_WARNING("Call on destroyed layer manager");
    return nullptr;
  }
  return RefPtr<TextLayer>(new TextLayerComposite(this)).forget();
}

already_AddRefed<BorderLayer>
LayerManagerComposite::CreateBorderLayer()
{
  if (LayerManagerComposite::mDestroyed) {
    NS_WARNING("Call on destroyed layer manager");
    return nullptr;
  }
  return RefPtr<BorderLayer>(new BorderLayerComposite(this)).forget();
}

LayerManagerComposite::AutoAddMaskEffect::AutoAddMaskEffect(Layer* aMaskLayer,
                                                            EffectChain& aEffects)
  : mCompositable(nullptr), mFailed(false)
{
  if (!aMaskLayer) {
    return;
  }

  mCompositable = ToLayerComposite(aMaskLayer)->GetCompositableHost();
  if (!mCompositable) {
    NS_WARNING("Mask layer with no compositable host");
    mFailed = true;
    return;
  }

  if (!mCompositable->AddMaskEffect(aEffects, aMaskLayer->GetEffectiveTransform())) {
    mCompositable = nullptr;
    mFailed = true;
  }
}

LayerManagerComposite::AutoAddMaskEffect::~AutoAddMaskEffect()
{
  if (!mCompositable) {
    return;
  }

  mCompositable->RemoveMaskEffect();
}

bool
LayerManagerComposite::IsCompositingToScreen() const
{
  if (!mCompositor) {
    return true;
  }
  return !mCompositor->GetTargetContext();
}

LayerComposite::LayerComposite(LayerManagerComposite *aManager)
  : HostLayer(aManager)
  , mCompositeManager(aManager)
  , mCompositor(aManager->GetCompositor())
  , mDestroyed(false)
  , mLayerComposited(false)
{ }

LayerComposite::~LayerComposite()
{
}

void
LayerComposite::Destroy()
{
  if (!mDestroyed) {
    mDestroyed = true;
    CleanupResources();
  }
}

void
LayerComposite::AddBlendModeEffect(EffectChain& aEffectChain)
{
  gfx::CompositionOp blendMode = GetLayer()->GetEffectiveMixBlendMode();
  if (blendMode == gfx::CompositionOp::OP_OVER) {
    return;
  }

  aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE] = new EffectBlendMode(blendMode);
  return;
}

bool
LayerManagerComposite::CanUseCanvasLayerForSize(const IntSize &aSize)
{
  return mCompositor->CanUseCanvasLayerForSize(gfx::IntSize(aSize.width,
                                                            aSize.height));
}

void
LayerManagerComposite::NotifyShadowTreeTransaction()
{
  if (gfxPrefs::LayersDrawFPS()) {
    mDiagnostics->AddTxnFrame();
  }
}

void
LayerComposite::SetLayerManager(HostLayerManager* aManager)
{
  HostLayer::SetLayerManager(aManager);
  mCompositeManager = static_cast<LayerManagerComposite*>(aManager);
  mCompositor = mCompositeManager->GetCompositor();
}

bool
LayerManagerComposite::AsyncPanZoomEnabled() const
{
  if (CompositorBridgeParent* bridge = mCompositor->GetCompositorBridgeParent()) {
    return bridge->GetOptions().UseAPZ();
  }
  return false;
}

bool
LayerManagerComposite::AlwaysScheduleComposite() const
{
  return !!(mCompositor->GetDiagnosticTypes() & DiagnosticTypes::FLASH_BORDERS);
}

nsIntRegion
LayerComposite::GetFullyRenderedRegion() {
  if (TiledContentHost* tiled = GetCompositableHost() ? GetCompositableHost()->AsTiledContentHost()
                                                        : nullptr) {
    nsIntRegion shadowVisibleRegion = GetShadowVisibleRegion().ToUnknownRegion();
    // Discard the region which hasn't been drawn yet when doing
    // progressive drawing. Note that if the shadow visible region
    // shrunk the tiled valig region may not have discarded this yet.
    shadowVisibleRegion.And(shadowVisibleRegion, tiled->GetValidRegion());
    return shadowVisibleRegion;
  } else {
    return GetShadowVisibleRegion().ToUnknownRegion();
  }
}

Matrix4x4
HostLayer::GetShadowTransform() {
  Matrix4x4 transform = mShadowTransform;
  Layer* layer = GetLayer();

  transform.PostScale(layer->GetPostXScale(), layer->GetPostYScale(), 1.0f);
  if (const ContainerLayer* c = layer->AsContainerLayer()) {
    transform.PreScale(c->GetPreXScale(), c->GetPreYScale(), 1.0f);
  }

  return transform;
}

bool
LayerComposite::HasStaleCompositor() const
{
  return mCompositeManager->GetCompositor() != mCompositor;
}

#ifndef MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS

/*static*/ bool
LayerManagerComposite::SupportsDirectTexturing()
{
  return false;
}

/*static*/ void
LayerManagerComposite::PlatformSyncBeforeReplyUpdate()
{
}

#endif  // !defined(MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS)

} // namespace layers
} // namespace mozilla